package com.bajiao.third.utils;

import cn.hutool.core.date.DateUtil;
import com.alipay.api.AlipayApiException;
import com.alipay.api.domain.*;
import com.alipay.api.internal.util.AlipaySignature;
import com.alipay.api.response.AlipayFundTransToaccountTransferResponse;
import com.alipay.api.response.AlipayTradePrecreateResponse;
import com.alipay.api.response.AlipayTradeRefundResponse;
import com.ijpay.alipay.AliPayApi;
import com.ijpay.alipay.AliPayApiConfig;
import com.ijpay.core.kit.HttpKit;
import com.ijpay.core.kit.PayKit;
import com.ijpay.core.kit.RsaKit;
import com.bajiao.common.exception.DataHandleException;
import com.bajiao.common.util.FastJsonHelper;
import com.bajiao.service.config.ConfigService;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang3.StringUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.stereotype.Component;
import org.springframework.transaction.annotation.Transactional;

import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.util.Date;
import java.util.HashMap;
import java.util.Map;

/**
 * <P>
 * 用途:支付宝支付工具类
 * 本工具类中提到 URLEncoder 编码的参数都需要在调用时直接编码好
 * </p>
 *
 * @author: FanJq
 * @create: 2024-10-10
 **/
@Slf4j
@Component
@Transactional(rollbackFor = Exception.class)
public class AliPayUtils {
    @Value("${bajiao.test-pay}")
    Integer testPay;

    @Value("${project-name}")
    private String projectName;

    @Autowired
    private ConfigService configServiceMove;

    /**
     * app支付
     *
     * @param outTradeNo 数据源id（用来表示支付的那个条数据） 为了和微信支付保持统一建议这么做：以订单举例 订单id记录中新增一个字段支付id 每次对该订单支付时随机生成一个id。多个订单一起支付时这个id需相同。
     * @param money      支付金额
     * @param body       商品信息
     * @param subject    商品名称
     * @param notifyUrl  回调地址
     * @param params     支付成功回调中的参数 （可以是任何数据） 建议进行编码 例如  URLEncoder.encode(paramsStr, "utf-8")
     * @return
     */
    public String payApp(String outTradeNo, String money, String body, String subject, String notifyUrl, String params) {
        if (testPay == 1) {
            money = "0.01";
        }
        //设置并获取阿里支付配置
        AliPayApiConfig aliPayApiConfig = configServiceMove.getAliPayApiConfig();

        try {
            AlipayTradeAppPayModel model = new AlipayTradeAppPayModel();
            model.setBody(body);//商品信息
            model.setSubject(subject);//商品名称
            model.setOutTradeNo(outTradeNo);
            model.setTimeoutExpress("30m");
            model.setTotalAmount(money);
            if (StringUtils.isNotBlank(params)) {
                model.setPassbackParams(params);
            }
            model.setProductCode("QUICK_MSECURITY_PAY");
            String orderInfo = AliPayApi.appPayToResponse(model, notifyUrl).getBody();
            return orderInfo;
        } catch (AlipayApiException e) {
            e.printStackTrace();
            throw new DataHandleException(e.getMessage());
        }
    }

    /**
     * h5支付 访问后会强制重定向至支付页面
     * <p>
     * 可能缺少 quit_url 参数（用户付款中途退出返回商户网站的地址）
     * 第三方框架demo没有该参数 支付api文档上有
     *
     * @param outTradeNo 数据源id（用来表示支付的那个条数据） 为了和微信支付保持统一建议这么做：以订单举例 订单id记录中新增一个字段支付id 每次对该订单支付时随机生成一个id。多个订单一起支付时这个id需相同。
     * @param money      支付金额
     * @param body       商品信息
     * @param subject    商品名称
     * @param notifyUrl  回调地址
     * @param returnUrl  支付成功后前端跳转路径
     * @param params     支付成功回调中的参数 （可以是任何数据） 建议进行编码 例如  URLEncoder.encode(paramsStr, "utf-8")
     */
    public void payH5(HttpServletResponse response, String outTradeNo, String money, String body, String subject, String notifyUrl, String returnUrl, String params) {
        if (testPay == 1) {
            money = "0.01";
        }
        //设置并获取阿里支付配置
        AliPayApiConfig aliPayApiConfig = configServiceMove.getAliPayApiConfig();
        if (aliPayApiConfig == null) {
            throw new DataHandleException("未获取到支付相关配置!");
        }
        try {
            Map<String, String> paramsMap = new HashMap<>();
            paramsMap.put("app_id", aliPayApiConfig.getAppId());
            paramsMap.put("method", "alipay.trade.wap.pay");
            paramsMap.put("return_url", returnUrl);
            paramsMap.put("charset", aliPayApiConfig.getCharset());
            paramsMap.put("sign_type", aliPayApiConfig.getSignType());
            paramsMap.put("timestamp", DateUtil.format(new Date(), "yyyy-MM-dd HH:mm:ss"));
            paramsMap.put("version", "1.0");
            paramsMap.put("notify_url", notifyUrl);


            Map<String, String> bizMap = new HashMap<>();
            bizMap.put("body", body);
            bizMap.put("subject", subject);
            bizMap.put("out_trade_no", outTradeNo);
            bizMap.put("total_amount", money);
            bizMap.put("product_code", "QUICK_WAP_WAY");
            if (StringUtils.isNotBlank(params)) {
                bizMap.put("passback_params", params);
            }
            paramsMap.put("biz_content", FastJsonHelper.toJSONString(bizMap));

            String content = PayKit.createLinkString(paramsMap);

            String encrypt = RsaKit.encryptByPrivateKey(content, aliPayApiConfig.getPrivateKey());
            paramsMap.put("sign", encrypt);

            String url = aliPayApiConfig.getServiceUrl() + "?" + PayKit.createLinkString(paramsMap, true);
            response.sendRedirect(url);
        } catch (Exception e) {
            e.printStackTrace();
        }
    }

    /**
     * 退款
     *
     * @param outTradeNo 订单编号
     * @param tradeNo    支付宝交易号
     * @param money      金额
     * @return
     */
    public boolean refundMoney(String outTradeNo, String tradeNo, String money) {

        //判断是否是测试支付
        if (testPay == 1) {
            money = "0.01";
        }

        //设置并获取阿里支付配置
        AliPayApiConfig aliPayApiConfig = configServiceMove.getAliPayApiConfig();
        if (aliPayApiConfig == null) {
            throw new DataHandleException("未获取到支付相关配置!");
        }

        AlipayTradeRefundResponse response = null;
        try {
            AlipayTradeRefundModel model = new AlipayTradeRefundModel();
            if (StringUtils.isNotEmpty(outTradeNo)) {
                model.setOutTradeNo(outTradeNo);
            }
            if (StringUtils.isNotEmpty(tradeNo)) {
                model.setTradeNo(tradeNo);
            }
            model.setRefundAmount(money);
            model.setRefundReason("正常退款");
            AliPayApi.tradeRefundToResponse(model);
        } catch (AlipayApiException e) {
            e.printStackTrace();
        }

        if (response == null) {
            log.error("退款失败..");
            return false;
        }

        if (response.isSuccess()) {
            log.info("退款成功..");
            log.info(response.getSubMsg());
            return true;
        }
        log.error("退款失败..");
        log.info(response.getSubMsg());
        return false;
    }

    /**
     * 单笔转账到支付宝账户(需要开通该功能才能使用)
     *
     * @param outTradeNo 商户端的唯一订单号(可以为提现的id)
     * @param type       收款账号标识 1：用户（UID） 2：支付宝登录号，支持邮箱和手机号格式
     * @param money      转账金额
     * @param account    账号
     * @param remark     备注
     * @return
     */
    public boolean transfer(String outTradeNo, Integer type, String money, String account, String remark) {

        //设置并获取阿里支付配置
        AliPayApiConfig aliPayApiConfig = configServiceMove.getAliPayApiConfig();
        if (aliPayApiConfig == null) {
            throw new DataHandleException("未获取到支付相关配置!");
        }

        AlipayFundTransToaccountTransferModel model = new AlipayFundTransToaccountTransferModel();
        model.setOutBizNo(outTradeNo);
        /**
         * 参与方的标识类型
         * ALIPAY_USER_ID：支付宝的会员ID
         * ALIPAY_LOGON_ID：支付宝登录号，支持邮箱和手机号格式
         */
        model.setPayeeType(type == 1 ? "ALIPAY_USER_ID" : "ALIPAY_LOGONID");
        model.setPayeeAccount(account);//收款账号
        model.setAmount(money);//转账账号
        //业务备注
        if (StringUtils.isNotBlank(remark)) {
            model.setRemark(remark);
        }

        AlipayFundTransToaccountTransferResponse response = null;
        try {
            response = AliPayApi.transferToResponse(model);
        } catch (Exception e) {
            e.printStackTrace();
        }
        if (response == null) {
            return false;
        }

        if (response == null) {
            log.error("转账失败..");
            return false;
        }

        if (response.isSuccess()) {
            log.info("转账成功..");
            log.info(response.getSubMsg());
            return true;
        }
        return true;
    }

    /**
     * 获取支付二维码
     *
     * @param outTradeNo 商户端的唯一订单号
     * @param money      支付金额
     * @param body       商品信息
     * @param subject    商品名称
     * @param notifyUrl  支付成功回调地址
     * @return
     */
    public String qrCode(String outTradeNo, String money, String body, String subject, String notifyUrl) {
        //设置并获取阿里支付配置
        AliPayApiConfig aliPayApiConfig = configServiceMove.getAliPayApiConfig();

        AlipayTradePrecreateModel model = new AlipayTradePrecreateModel();
        model.setSubject(subject);
        model.setBody(body);
        model.setTotalAmount(money);
        model.setTimeoutExpress("5m");
        model.setOutTradeNo(outTradeNo);
        AlipayTradePrecreateResponse response = null;
        try {
            response = AliPayApi.tradePrecreatePayToResponse(model, notifyUrl);
        } catch (Exception e) {
            e.printStackTrace();
        }
        if (response == null) {
            log.error("支付宝扫码调用失败");
            return "";
        }

        if (!response.isSuccess()) {
            log.error("支付宝扫码调用失败");
            log.error(response.getSubMsg());
            return "";
        }

        log.error("支付宝扫码调用失败");
        log.error(response.getSubMsg());
        return response.getQrCode();
    }

    /**
     * 解析支付回调通知 数据
     *
     * @param request
     * @return
     */
    public Map payNotify(HttpServletRequest request) {
        //设置并获取阿里支付配置
        AliPayApiConfig aliPayApiConfig = configServiceMove.getAliPayApiConfig();

        Map<String, String> params = HttpKit.toMap(request);
        // 调用SDK验证签名
        boolean signVerified;
        try {
            signVerified = AlipaySignature.rsaCheckV1(params, aliPayApiConfig.getAliPayPublicKey(),
                    aliPayApiConfig.getCharset(), aliPayApiConfig.getSignType());
        } catch (AlipayApiException e) {
            e.printStackTrace();
            throw new DataHandleException(e.getErrMsg());
        }
        if (!signVerified) {
            throw new DataHandleException("验签失败");
        }
        return params;
    }


    /**
     * 关闭订单(订单超时未支付、更换别的支付方式时调用该方法) 只有用户输入了密码后才会创建订单
     *
     * @param outTradeNo 统一下单时传入的参数，可以是付款编号或订单编号，商品订单为付款编号
     * @return
     */
    public void tradeClose(String outTradeNo) {
        //设置并获取阿里支付配置
        AliPayApiConfig aliPayApiConfig = configServiceMove.getAliPayApiConfig();
        try {
            AlipayTradeCloseModel model = new AlipayTradeCloseModel();
            if (StringUtils.isNotEmpty(outTradeNo)) {
                model.setOutTradeNo(outTradeNo);
            }
            AliPayApi.tradeCloseToResponse(model);
        } catch (AlipayApiException e) {
            e.printStackTrace();
        }
    }

}
